package net.penguinmenac3.math.rings;

import net.penguinmenac3.math.MathObject;

/**
 * Represents a complex number.
 * (re+im*i)
 * @author Michael
 */
public class ComplexNumber implements MathObject {
	private RealNumber re;
	private RealNumber im;

	public ComplexNumber(RealNumber re, RealNumber im) {
		if (re == null || im == null) {
			throw new NullPointerException();
		}
		this.re = re;
		this.im = im;
	}

	public ComplexNumber(double re, double im) {
		this(new RealNumber(re), new RealNumber(im));
	}

	public ComplexNumber(double re) {
		this(re, 0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see penguinmenac3.math.MathObject#add(penguinmenac3.math.MathObject)
	 */
	@Override
	public MathObject add(MathObject o) {
		if (isNullElement()) {
			return o.clone();
		}
		if (o.isNullElement()) {
			return this.clone();
		}
		ComplexNumber other;
		if (o instanceof RealNumber) {
			other = new ComplexNumber((RealNumber) o, new RealNumber(0));
		} else if (o instanceof ComplexNumber) {
			other = (ComplexNumber) o;
		} else {
			throw new UnsupportedOperationException();
		}
		RealNumber newRe = (RealNumber) re.add(other.re);
		RealNumber newIm = (RealNumber) im.add(other.im);
		return new ComplexNumber(newRe, newIm);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * penguinmenac3.math.MathObject#subtract(penguinmenac3.math.MathObject)
	 */
	@Override
	public MathObject subtract(MathObject o) {
		if (o.isNullElement()) {
			return this.clone();
		}
		if (isNullElement()) {
			return o.additiveInverse();
		}
		ComplexNumber other;
		if (o instanceof RealNumber) {
			other = new ComplexNumber((RealNumber) o, new RealNumber(0));
		} else if (o instanceof ComplexNumber) {
			other = (ComplexNumber) o;
		} else {
			throw new UnsupportedOperationException();
		}
		RealNumber newRe = (RealNumber) re.subtract(other.re);
		RealNumber newIm = (RealNumber) im.subtract(other.im);
		return new ComplexNumber(newRe, newIm);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * penguinmenac3.math.MathObject#multiply(penguinmenac3.math.MathObject)
	 */
	@Override
	public MathObject multiply(MathObject o) {
		if (o.isNeutralElement()) {
			return this.clone();
		}
		if (isNeutralElement()) {
			return o.clone();
		}
		if (isNullElement() || o.isNullElement()) {
			return new ComplexNumber(0);
		}
		ComplexNumber other;
		if (o instanceof RealNumber) {
			other = new ComplexNumber((RealNumber) o, new RealNumber(0));
		} else if (o instanceof ComplexNumber) {
			other = (ComplexNumber) o;
		} else {
			throw new UnsupportedOperationException();
		}
		RealNumber newRe = (RealNumber) re.multiply(other.re);
		RealNumber newRe2 = (RealNumber) im.multiply(other.im);
		RealNumber newIm = (RealNumber) im.multiply(other.re);
		RealNumber newIm2 = (RealNumber) re.multiply(other.im);
		newRe = (RealNumber) newRe.subtract(newRe2);
		newIm = (RealNumber) newIm.add(newIm2);
		return new ComplexNumber(newRe, newIm);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see penguinmenac3.math.MathObject#divide(penguinmenac3.math.MathObject)
	 */
	@Override
	public MathObject divide(MathObject o) {
		if (o.isNullElement()) {
			throw new IllegalArgumentException("Division by ZERO");
		}
		if (o.isNeutralElement()) {
			return this.clone();
		}
		if (isNullElement()) {
			return new ComplexNumber(0);
		}
		ComplexNumber other;
		if (o instanceof RealNumber) {
			other = new ComplexNumber((RealNumber) o, new RealNumber(0));
		} else if (o instanceof ComplexNumber) {
			other = (ComplexNumber) o;
		} else {
			throw new UnsupportedOperationException();
		}
		ComplexNumber self = (ComplexNumber) this.multiply(other.conjugate());
		other = (ComplexNumber) o.multiply(other.conjugate());
		RealNumber newRe = (RealNumber) self.re.divide(other.re);
		RealNumber newIm = (RealNumber) self.im.divide(other.re);
		return new ComplexNumber(newRe, newIm);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see penguinmenac3.math.MathObject#isNeutralElement()
	 */
	@Override
	public boolean isNeutralElement() {
		return re.isNeutralElement() && im.isNullElement();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see penguinmenac3.math.MathObject#isNullElement()
	 */
	@Override
	public boolean isNullElement() {
		return re.isNullElement() && im.isNullElement();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see penguinmenac3.math.MathObject#getValue()
	 */
	@Override
	public String getValue() {
		return toString();
	}

	@Override
	public String toString() {
		if (im.isNullElement()) {
			return re.toString();
		}
		if (re.isNullElement()) {
			return im.toString()+"i";
		}
		return re.toString() + "+" + im.toString() + "i";
	}

	@Override
	public ComplexNumber clone() {
		return new ComplexNumber(re, im);
	}

	/**
	 * Determine the conjugate complex number.
	 * @return
	 */
	public ComplexNumber conjugate() {
		return new ComplexNumber(re, (RealNumber)im.additiveInverse());
	}
	
	@Override
	public MathObject inverse() {
		ComplexNumber one = new ComplexNumber(1);
		return one.divide(this);
	}
	
	@Override
	public MathObject additiveInverse() {
		return new ComplexNumber((RealNumber)re.additiveInverse(), (RealNumber)im.additiveInverse());
	}
}
